package le.grand;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import de.schlichtherle.io.FileReader;

public class FileParser {
	private static final SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy", new Locale(
			"en, EN"));

	String[] lines;
	int maxConsideredLineLength = 0;
	Date date;

	int index;

	private FlightInfo flightInfo;

	private Set<String> roomTypes = new HashSet<>();

	HotelInfo currentHotel;
	ReservationInfo currentReservation;

	public FlightInfo parse(FileReader fr) throws Exception {
		String fileContent = IOUtils.toString(fr);
		fr.close();
		lines = StringUtils.split(fileContent, "\n\r");
		for (int i = 0; i < lines.length; i++) {
			lines[i] = lines[i].trim();
		}
		resolveMaxConsideredLineLength();
		
		int firstDateLine = firstDateLine(lines);
		index = firstDateLine + 2;
		parseFlightInfo(firstDateLine);
		readRoomTypes();

		String hotel = null;
		while ((hotel = nextHotel()) != null) {
			System.out.println("New Hotel: " + hotel);
			currentHotel = getHotelInfo(hotel);
			currentReservation = null;
			while (readReservationLine()) {

			}
		}
		return flightInfo;
	}

	private void resolveMaxConsideredLineLength() {
		for(String s: lines) {
			if(s.startsWith("PNR ")) {
				maxConsideredLineLength = s.indexOf("Nights") + 6;
			}
		}
	}
	
	private void readRoomTypes() {
		for (int i = lines.length - 1; i > 0; i--) {
			if (lines[i].startsWith("Grand Total:")) {
				System.out.println("Room types: " + roomTypes);
				return;
			}
			readRoomTypesLine(lines[i]);
		}
	}

	private void readRoomTypesLine(String s) {
		String[] toks = StringUtils.split(s, " ");
		for (int i = 0; i < toks.length; i++) {
			int ix = toks[i].indexOf("=");
			if (ix > 0) {
				roomTypes.add(toks[i].substring(0, ix));
			}
		}

	}

	private void parseFlightInfo(int firstDateLine) throws ParseException {
		flightInfo = new FlightInfo();
		String l = lines[firstDateLine];
		String dateStr = l.substring(0, 12).trim();
		Date date = sdf.parse(dateStr);
		String[] toks = StringUtils.split(l.substring(13));
		flightInfo.setArrival(toks[toks.length - 1]);
		flightInfo.setDeparture(toks[toks.length - 2]);
		flightInfo.setDay(date);
		StringBuilder sb = new StringBuilder();
		for (int i = 1; i < toks.length - 2; i++) {
			sb.append(toks[i]);
			sb.append(" ");
		}
		flightInfo.setFlightName(sb.toString());
		System.out.println("Fight: " + flightInfo);
	}

	public HotelInfo getHotelInfo(String hotelName) {
		HotelInfo hotelInfo = flightInfo.getHotels().get(hotelName);
		if (hotelInfo == null) {
			hotelInfo = new HotelInfo(hotelName);
			flightInfo.getHotels().put(hotelName, hotelInfo);
		}
		return hotelInfo;
	}

	private boolean readReservationLine() {
		String l = lines[index];
		if(l.length() > maxConsideredLineLength) {
			l = l.substring(0, maxConsideredLineLength);
		}
		if (l.startsWith("Total passengers for Hotel:")) {
			return false;
		}
		if (l.startsWith("-------------------")) {
			index++;
			return true;
		}
		String[] tokens = StringUtils.split(l, " ");
		if (tokens.length == 0) {
			index++;
			return true;
		}
		
		if (tokens[0].equals("Print") || tokens[0].equals("Arrival") || tokens[0].equals("Sort")
				|| tokens[0].equals("PNR") || tokens[0].equals("FOR")) {
			index++;
			return true;
		}

		if (StringUtils.isNumeric(tokens[0])) {
			currentReservation = new ReservationInfo();
			Integer reservationNumber = Integer.parseInt(tokens[0]);
			currentReservation.setReservationNumber(reservationNumber);
			int nextNumericToken = getNextNumericToken(1, tokens);
			addNames(tokens, 1, nextNumericToken - 1);
			Integer numOfPassengers = Integer.parseInt(tokens[nextNumericToken]);
			currentReservation.setNumOfPassengers(numOfPassengers);
			currentHotel.addReservation(currentReservation);
		} else if (currentReservation != null) {
			int nextNumericToken = getNextNumericToken(1, tokens);

			if (tokens.length <= nextNumericToken) {
				System.out.println();
			}
			System.out.println(">>> " + Arrays.toString(tokens));
			// if has room type, then this is a continuation
			if (tokens.length > nextNumericToken && isRoomType(tokens[nextNumericToken + 1])) {
				Integer numOfPassengers = Integer.parseInt(tokens[nextNumericToken]);
				addNames(tokens, 0, nextNumericToken - 1, numOfPassengers);
			} else if (!hasNoNumericsNorSlashes(tokens)) {
				addNames(tokens);
			}
		}
		index++;
		return true;
	}

	private boolean hasNoNumericsNorSlashes(String[] tokens) {
		for (String s : tokens) {
			if (StringUtils.isNumeric(s)) {
				return false;
			}
			if (s.indexOf("/") >= 0) {
				return false;
			}
		}
		return true;
	}

	private boolean isRoomType(String s) {
		return roomTypes.contains(s);
		// try {
		// RoomType.valueOf(s);
		// return true;
		// } catch (Exception e) {
		// return false;
		// }
	}

	private void addNames(String[] tokens) {
		for (int i = 0; i < tokens.length; i++) {
			currentReservation.addNameToLastEntry(tokens[i]);
		}
		ReservationEntry lastEntry = currentReservation.getLastEntry();
		System.out.println("Append to reservation: " + lastEntry.getNames());
	}

	private void addNames(String[] tokens, int start, int end) {
		for (int i = start; i < end + 1; i++) {
			currentReservation.addNameToLastEntry(tokens[i]);
		}
	}

	private void addNames(String[] tokens, int start, int end, int numOfPassengers) {
		currentReservation.addEntry(numOfPassengers);
		for (int i = start; i < end + 1; i++) {
			currentReservation.addNameToLastEntry(tokens[i]);
		}
		ReservationEntry lastEntry = currentReservation.getLastEntry();
		System.out.println("Append to reservation: " + lastEntry.getNames() + " numOfPassengers="
				+ lastEntry.getNumOfPassengers());
	}

	private int getNextNumericToken(int startIndex, String[] tokens) {
		for (int i = startIndex; i < tokens.length; i++) {
			if (StringUtils.isNumeric(tokens[i])) {
				return i;
			}
		}
		return -1;
	}

	private int firstDateLine(String[] splits) {
		for (int i = 0; i < splits.length; i++) {
			if (splits[i].startsWith("===============")) {
				return i - 1;
			}
		}
		return -1;
	}

	private String nextHotel() {
		index = nextHotelLine(index);
		if (index < 0)
			return null;
		String s = StringUtils.replace(lines[index], "Hotel", "");
		int i = s.indexOf("Arr.");
		index++;
		return s.substring(0, i - 1).trim();
	}

	private int nextHotelLine(int from) {
		for (int i = from; i < lines.length; i++) {
			if (lines[i].startsWith("Hotel")) {
				return i;
			}
		}
		return -1;
	}
}
